/*
 * Copyright (c) 2024. hallele, Inc All rights reserved.
 */

package cn.hallele.infrastructure.util;

import cn.hallele.infrastructure.util.annotation.tree.Tree;
import cn.hallele.infrastructure.util.annotation.tree.TreeId;
import cn.hallele.infrastructure.util.annotation.tree.TreeParentId;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.hutool.core.text.StrUtil;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 常用的一些字符串处理、比较、格式转换等处理
 *
 * @author anle5
 * @since 2024/9/11 22:31
 */
@Slf4j
public class ObjectUtil extends StringUtils {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES);
        OBJECT_MAPPER.registerModule(new JavaTimeModule());
    }

    /**
     * 不允许new
     */
    @Deprecated
    public ObjectUtil() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * 判断是否是null,忽略key的大小写
     *
     * @param collection 集合
     * @param key        判断值
     * @return true: null   false: 非 null
     */
    public static boolean containsIgnoreCase(Collection<?> collection, String key) {
        if (isEmpty(collection)) {
            return false;
        }
        return collection.contains(key)
                || collection.contains(key.toLowerCase())
                || collection.contains(key.toUpperCase());
    }

    /**
     * 判断是否是null
     *
     * @param obj 判断值
     * @return true: null   false: 非 null
     */
    public static boolean isNull(Object obj) {
        return obj == null;
    }

    /**
     * 判断是否是空集合
     *
     * @param collection 判断值
     * @return true: 空   false: 非 null
     */
    public static boolean isEmpty(Collection<?> collection) {
        return collection == null || collection.isEmpty();
    }

    /**
     * 判断是否是非空集合
     *
     * @param collection 判断值
     * @return true: 非空   false: 空
     */
    public static boolean isNotEmpty(Collection<?> collection) {
        return !isEmpty(collection);
    }

    /**
     * 集合转HashSet
     *
     * @param collection 集合
     * @param <T>        泛型
     * @return HashSet
     */
    public static <T> Set<T> toSet(Collection<T> collection) {
        if (isEmpty(collection)) {
            return Collections.emptySet();
        }

        return new HashSet<>(collection);
    }


    /**
     * 集合转ArrayList
     *
     * @param collection 集合
     * @param <T>        泛型
     * @return ArrayList
     */
    public static <T> List<T> toList(Collection<T> collection) {
        if (isEmpty(collection)) {
            return Collections.emptyList();
        }

        return new ArrayList<>(collection);
    }

    /**
     * 判断是否是null
     *
     * @param obj 判断值
     * @return true: 非 null  false: null
     */
    public static boolean isNotNull(Object obj) {
        return !isNull(obj);
    }

    /**
     * 判断是否相等，底层调用的obj1的 equals 方法
     *
     * @param obj1 对比方
     * @param obj2 要与 {@code obj1} 进行比较以求相等的对象
     * @return true: 相同  false: 不相同
     */
    public static boolean equals(Object obj1, Object obj2) {
        return Objects.equals(obj1, obj2);
    }

    /**
     * 去掉指定前缀
     *
     * @param str    字符串，空返回原字符串
     * @param prefix 前缀，空返回原字符串
     * @return 切掉后的字符串，若前缀不是 prefix， 返回原字符串
     */
    public static String removePrefix(final CharSequence str, final CharSequence prefix) {
        return StrUtil.removePrefix(str, prefix, false);
    }

    /**
     * 强转方法
     *
     * @param object object
     * @param <T>    泛型
     * @return 目标
     */
    @SuppressWarnings("unchecked")
    public static <T> T cast(Object object) {
        return (T) object;
    }

    /**
     * 将bean转成json string,如果无法转成json string，将会使用toString方法转成普通string
     *
     * @param object object
     * @return jsonString
     */
    public static String toJsonString(Object object) {
        try {
            return OBJECT_MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.error("Object {} can not transfer to json string", object.getClass(), e);
            return object.toString();
        }
    }

    /**
     * 将json string转成bean
     *
     * @param jsonString object
     * @param tClass     class
     * @return jsonString
     */
    public static <T> T readValue(String jsonString, Class<T> tClass) {
        try {
            return OBJECT_MAPPER.readValue(jsonString, tClass);
        } catch (JsonProcessingException e) {
            log.error("JsonString can not transfer to object {} ", tClass, e);
            return null;
        }
    }

    /**
     * bean转换,使用json序列化 反序列化的方式转换
     *
     * @param ori    原对象
     * @param tClass 目标class
     * @param <T>    目标值
     * @return 转换后的目标值
     */
    public static <T> T transferBean(Object ori, Class<T> tClass) {
        if (ori == null) {
            return null;
        }
        return readValue(toJsonString(ori), tClass);
    }

    /**
     * bean转换,使用json序列化 反序列化的方式转换
     *
     * @param objects 原对象集合
     * @param tClass  目标class
     * @param <T>     目标值
     * @return 转换后的目标值
     */
    public static <T> List<T> transferBeanList(List<?> objects, Class<T> tClass) {
        if (CollectionUtils.isEmpty(objects)) {
            return Collections.emptyList();
        }

        return objects.stream().map(o -> readValue(toJsonString(o), tClass)).toList();
    }

    /**
     * 将T转成R，T=null时返回null
     *
     * @param t        t
     * @param function lambda 表达式
     * @param <T>      原始值
     * @param <R>      目标值
     * @return R
     */
    public static <T, R> R map(T t, Function<T, R> function) {
        return t == null ? null : function.apply(t);
    }

    /**
     * 格式化sql模糊查询字符
     *
     * @param param 参数
     * @return 如果参数为 null，则返回一个百分号：“%”，否则返回自身
     */
    public static <T> T parseDBParam(Object param) {
        return param == null ? cast("%") : cast(param);
    }

    /**
     * 将平铺的list转成tree结构的list，对应的bean需要{@link TreeId}、{@link Tree}和{@link TreeParentId} 三个注解支持
     *
     * @param list           列表
     * @param topParentIdVal 顶层节点父id的值
     * @return tree结构的list
     */
    public static <T> List<T> toTree(List<T> list, Object topParentIdVal) {
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }

        Class<?> aClass = list.getFirst().getClass();
        Field id = null;
        Field parentId = null;
        Field children = null;
        for (Field field : aClass.getDeclaredFields()) {
            if (id != null && parentId != null && children != null) {
                break;
            }

            if (field.getAnnotation(Tree.class) != null && children == null) {
                children = field;
            }

            if (field.getAnnotation(TreeParentId.class) != null && parentId == null) {
                parentId = field;
            }

            if (field.getAnnotation(TreeId.class) != null && id == null) {
                id = field;
            }
        }

        if (id == null || parentId == null || children == null) {
            log.error("Can not to Tree Map.One or more then one of the follow params is null, id: {}, parentId: {}, children: {}",
                    id, topParentIdVal, children);
            throw new UnsupportedOperationException("Can not convert to Tree Map");
        }

        return getTreeList(list, topParentIdVal, parentId, id, children);
    }

    /**
     * @param list           list
     * @param topParentIdVal topParentIdVal
     * @param parentIdName   parentIdName
     * @param idName         idName
     * @param childrenName   childrenName
     * @param <T>            T
     * @return TreeList
     */
    private static <T> List<T> getTreeList(List<T> list, Object topParentIdVal, Field parentIdName, Field idName, Field childrenName) {
        Map<String, List<T>> mapList = list.stream()
                .collect(Collectors.groupingBy(o -> getFieldValue(o, parentIdName).toString()));
        list.forEach(node -> setFieldValue(node, mapList.get(getFieldValue(node, idName).toString()), childrenName.getName()));
        return list.stream()
                .filter(o -> Objects.equals(topParentIdVal, getFieldValue(o, parentIdName)))
                .collect(Collectors.toList());
    }

    /**
     * 获取属性值
     *
     * @param o     对象
     * @param field 属性
     * @return Object
     */
    private static Object getFieldValue(Object o, Field field) {
        try {
            field.setAccessible(true);
            return field.get(o);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置字段值
     *
     * @param o         对象
     * @param val       值
     * @param fieldName 属性名
     */
    private static void setFieldValue(Object o, Object val, String fieldName) {
        try {
            Class<?> oClass = o.getClass();
            Field field = oClass.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(o, val);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
